﻿using System;
using System.Windows;

using Game.Common;
using Game.Map;
using Game.Map.Extensions;

namespace Game.PathFinder.Internal
{
    class Node : IComparable<Node>
    {
        readonly IGameMap _gameMap;
        readonly Node _parent;
        readonly Point _point;
        readonly AStarFinder _finder;
        readonly double _cost;
        readonly double _terrianCost;
        readonly double _distanceToStart;

        public Node(
            IGameMap map,
            Node parent, 
            Point point, 
            AStarFinder finder, 
            double terrianCost)
        {
            _gameMap = map;
            _terrianCost = terrianCost;
            _parent = parent;
            _point = point;
            _finder = finder;
            _distanceToStart = DistanceToStart;
            _cost = CalculateCost();
        }
        
        public Node Parent
        {
            get { return _parent; }
        }
        
        public Point Point
        {
            get { return _point; }
        }
        
        public double Cost
        {
            get { return _cost; }
        }
        
        private double CalculateCost()
        {
            return (DistanceToGoal + DistanceToStart);
        }
        
        double DistanceToGoal
        {
            get { return Distance(_finder.GoalField.Point, _point); }
        }
        
        double DistanceToStart
        {
            get 
            {
                if (_distanceToStart > 0)
                {
                    return _distanceToStart;
                }
                
                double v = _terrianCost;
                GameDebug.Assert(_terrianCost >= 0);

                if (_parent != null)
                {
                    v += _parent.DistanceToStart;
                }
                
                return v;
            }
        }
        
        double Distance(Point point1, Point point2)
        {
            var path = GamePath.CreateDirectPath(point1, point2);
            return path.CalcTerrainCost(_gameMap);
        }
        
        public int CompareTo(Node other)
        {
            return (int)(Cost - other.Cost);
        }
        
        public override string ToString()
        {
            return "[" + _point + ":c=" + _cost + "]";
        }

#region Equals and GetHashCode implementation

        public override int GetHashCode()
        {
            return _point.GetHashCode();
        }
        
        public override bool Equals(object obj)
        {
            Node other = obj as Node;
            if (other == null)
                return false;
            return (_point == other._point);
        }
        
        public static bool operator ==(Node lhs, Node rhs)
        {
            if (ReferenceEquals(lhs, rhs))
            {
                return true;
            }
            if (ReferenceEquals(lhs, null) || ReferenceEquals(rhs, null))
                return false;
            return lhs.Equals(rhs);
        }
        
        public static bool operator !=(Node lhs, Node rhs)
        {
            return !(lhs == rhs);
        }
#endregion
        
    }
}
